package cn.workreport.interceptor;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.workreport.modules.common.annotations.PassToken;
import cn.workreport.modules.common.annotations.UserLoginToken;
import cn.workreport.config.conf.TokenConfiguration;
import cn.workreport.modules.common.enums.IsEnum;
import cn.workreport.modules.permission.entity.Permission;
import cn.workreport.modules.permission.enums.PermissionEnum;
import cn.workreport.modules.permission.service.IPermissionService;
import cn.workreport.modules.role.entity.Role;
import cn.workreport.modules.role.service.IRoleService;
import cn.workreport.modules.users.entity.UserEntity;
import cn.workreport.modules.common.exception.HasNoPermissionException;
import cn.workreport.modules.common.exception.TokenAuthExpiredException;
import cn.workreport.modules.users.service.IUserService;
import cn.workreport.util.TokenUtil;
import cn.workreport.util.UserChacheFromToken;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Log4j2
@Component
public class AuthHandlerInterceptor implements HandlerInterceptor {
    @Autowired
    IUserService userService;

//    @Autowired
//    IPermissionService permissionService;

    @Autowired
    IRoleService roleService;

    @Autowired
    TokenUtil tokenUtil;

//    @Value("${token.privateKey}")
//    private String privateKey;
//
//    @Value("${token.yangToken}")
//    private Long yangToken;
//
//    @Value("${token.oldToken}")
//    private Long oldToken;

    @Autowired
    TokenConfiguration tokenConfiguration;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.err.println("======== 进入拦截器 ========");
        String token = request.getHeader("login-token");
        System.out.println("\t请求的 token ===>" + token);

        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        Class clazz = handlerMethod.getBeanType();

        // 1. 检查请求的【方法】中是否有 passtoken 注解，有则直接跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }

        // 2. 检查请求的【方法】或者【类】中有没有需要用户权限 UserLoginToken 的注解
        if (method.isAnnotationPresent(UserLoginToken.class) || clazz.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken;
            if (method.isAnnotationPresent(UserLoginToken.class)) {
                userLoginToken = method.getAnnotation(UserLoginToken.class);
            } else {
                userLoginToken = (UserLoginToken) clazz.getAnnotation(UserLoginToken.class);
            }
            if (userLoginToken.required()) {

                // 执行 token 认证
                if (null == token || "".equals(token.trim())) {
                    throw new TokenAuthExpiredException("需要登录才能访问，请登录！");
                }

                Map<String, Long> tokenDataMap = tokenUtil.parseToken(token);
                Integer userId = Math.toIntExact(tokenDataMap.get("userId"));

                long timeOfToken = System.currentTimeMillis() - tokenDataMap.get("timeStamp");
                // 1.判断 token 是否过期
                // 年轻 token
                if (timeOfToken < tokenConfiguration.getYangToken()) {
//                   log.info("token 未过期且不需要刷新");
                    System.out.println("\t年轻 token 不需要刷新");
                }
                // 老年 token 就刷新 token
                else if (timeOfToken >= tokenConfiguration.getYangToken() && timeOfToken <= tokenConfiguration.getOldToken()) {
                    System.out.println("\t老年 token 需要刷新");
                    response.addHeader("login-token", tokenUtil.getToken(userId));
                }
                // 过期 token 就返回 token 无效
                else {
                    throw new TokenAuthExpiredException("token 已过期，请重新登录！");
                }

                // 根据 token 中的 userId 获取用户信息
                UserEntity user = userService.getById(userId);

                // 拦截不存在或已被停用的用户
                if (ObjectUtil.isEmpty(user) || IsEnum.YES.equals(user.getDeleted())) {
                    throw new TokenAuthExpiredException("用户不存在，请重新登录");
                }

                // 把 用户信息 存在当前线程的缓存中
                UserChacheFromToken.setUser(user);

                // 超级管理员跳过权限验证
                if (!ObjectUtil.isEmpty(user.getIsSupperAdmin()) && user.getIsSupperAdmin()) {
                    log.info("超级管理员跳过权限验证");
                    return true;
                }

                // 2.角色匹配
                PermissionEnum[] needPermissionList = userLoginToken.permission();
                System.err.println("\t当前接口需要的权限 ====>" + Arrays.toString(needPermissionList));

                // 接口需要权限
                if (needPermissionList.length > 0) {
                    // 因为角色权限是跟机构绑定，如果没有绑定机构，则优先提示机构未绑定
                    if (ObjectUtil.isEmpty(user.getOrgId())) {
                        throw new HasNoPermissionException("当前用户未关联机构，请先关联");
                    }
                    if (ObjectUtil.isEmpty(user.getRoleId())) {
                        throw new HasNoPermissionException("当前用户未关联角色，请联系管理员");
                    }
                    Role userRole = roleService.getById(user.getRoleId());
                    if (ObjectUtil.isEmpty(userRole)) {
                        throw new HasNoPermissionException("当前用户关联角色不存在，请联系管理员");
                    }
                    List<String> userPermissionList = userRole.getPermissions();
                    log.info("当前用户权限列表 ===>" + userPermissionList);
                    for (PermissionEnum needPermission: needPermissionList) {
                        for (String userPermission: userPermissionList) {
                            if (needPermission.getValue().equals(userPermission)) {
                                return true;
                            }
                        }
                    }
                    throw new HasNoPermissionException("抱歉，当前用户没有权限，请联系管理员");
                }

                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
       // 执行结束后释放 ThreadLocal 资源防止oom(资源溢出)
        UserChacheFromToken.removeUser();
    }
}
